package com.thientan.controller

import grails.converters.JSON
import groovy.sql.Sql

import com.thientan.model.ChiTietThuChi
import com.thientan.model.HopDong
import com.thientan.model.NhanVien
import com.thientan.model.enums.LoaiChiTietThuChi
import com.thientan.model.enums.LoaiNhanVien
import com.thientan.model.enums.TrangThaiHopDong
import com.thientan.service.NhanVienService
import com.thientan.utils.Constants
import com.thientan.utils.Utility

class NhanVienController extends AbstractBaseController{
	def dataSource
	NhanVienService nhanVienService

	Class getDomainClass(){
		NhanVien.class
	}

	@Override
	public Object getDomainService() {
		nhanVienService
	}

	@Override
	public String getDomainName() {
		message(code : "default.thientan.messages.nhanVien")
	}

	@Override
	public String getDisplayFieldName() {
		"maNv"
	}

	def initData() {
		Map result = doInitializeData()
		render result as JSON
	}

	def initDataWithListNhanVien = {
		Map result = doInitializeData()
		List<NhanVien> nvs = NhanVien.list()

		result.putAt("nhanVien", extractPropertiesAsMaps(nvs, ["id", "maNv"]))
		render result as JSON
	}

	def loadNhanVien = {
		NhanVien nv = NhanVien.get(params["id"])
		Map result = extractPropertiesAsMap(nv, [
			"id",
			"maNv",
			"hoTen",
			"doiTruong.id",
			"kichHoat"
		])
		result.putAt('loaiNhanVien', nv.loaiNhanVien.value)
		result.putAt('password',  nv.password.decodePassword())
		putOrUpdateToSession(nv, nv.id)
		render result as JSON
	}

	def loadDoiTruongAndNvBanHang = {
		List<NhanVien> doiTruongs = NhanVien.findAllWhere("doiTruong" : null, "kichHoat" : true, "loaiNhanVien" : LoaiNhanVien.BAN_HANG)
		List<NhanVien> nvBans = NhanVien.findAllWhere("kichHoat" : true, "loaiNhanVien" : LoaiNhanVien.BAN_HANG)
		Map result = ["doiTruongs" : extractPropertiesAsMaps(doiTruongs, ["id", "maNv"]),
			"nhanViens" : extractPropertiesAsMaps(nvBans, ["id", "maNv", "doiTruong.id"])]
		render result as JSON
	}

	def loadDoiTruong = {
		List<NhanVien> doiTruongs = NhanVien.findAllWhere("doiTruong" : null, "kichHoat" : true, "loaiNhanVien" : LoaiNhanVien.BAN_HANG)
		render extractPropertiesAsMaps(doiTruongs, ["id", "maNv"]) as JSON
	}

	protected Map doInitializeData(){
		List loaiNv = LoaiNhanVien.values().collect {
			["id" : it.value, "loaiNv" : it.displayText]
		}
		["success" : true, "loaiNv" : loaiNv]
	}

	protected Map prepareParams(){
		Map parameters = params

		if(params['doiTruong-id']){
			params['doiTruong.id'] = params['doiTruong-id']
		} else {
			params['doiTruong'] = null
		}


		parameters.putAt("loaiNhanVien", LoaiNhanVien.valueOf(params['loaiNv-id']))
		parameters.putAt("kichHoat", params['kichHoat'] != null)

		parameters.putAt("password", params['password'].encodeAsPassword())
		parameters.putAt("maNv", params['maNv'].toUpperCase())

		parameters
	}

	protected Map buildExportData(Map exportParams) {
		def sdfToDate = new java.text.SimpleDateFormat(Constants.TO_DATE_FORMAT)
		def sdfDateTo = new java.text.SimpleDateFormat(Constants.DEFAULT_DATE_FORMAT)
		Date dateFrom = sdfToDate.parse(params["ngayChamFrom"])
		// Keep the to date value instead of after increased one.
		Date dateToOriginal = sdfToDate.parse(params["ngayChamTo"])
		Date dateTo = Utility.increaseDays(dateToOriginal)
		String dateFromSql = sdfToDate.format(dateFrom)
		String dateToSql = sdfToDate.format(dateTo)

		Long dtId = params.long("nvDoiTruong-id")
		NhanVien doiTruong = NhanVien.get(dtId)
		Map exportData = ["data" : [],
			"maDt" : doiTruong.maNv,
			"thoiGian" : sdfDateTo.format(dateFrom) + " - " + sdfDateTo.format(dateToOriginal)]

		List nvs = NhanVien.withCriteria {
			projections {
				property("id")
				property("hoTen")
				property("maNv")
			}
			eq("loaiNhanVien", LoaiNhanVien.BAN_HANG)
			eq("kichHoat", true)
			or {
				eq("doiTruong.id", dtId)
				eq("id", dtId)
			}
		}
		
		// Count 0.5 for double saler.
		String sql = "select nv, count(hop_dong_id) from (" +
					 "select hop_dong_id, nhan_vien1_id as nv from hop_dong " +
					 "where doi_truong_id=${dtId} and ngay_ban between '${dateFromSql}' and '${dateToSql}' " +
					 "and trang_thai <> 'DONG' " +
					 "union all " +
					 "select hop_dong_id, nhan_vien2_id as nv from hop_dong " +
					 "where doi_truong_id=${dtId} and ngay_ban between '${dateFromSql}' and '${dateToSql}' " +
					 "and trang_thai <> 'DONG') " +
					 "as hd group by nv;"
		Map nv2NumHd = [:]
		new Sql(dataSource).rows(sql)?.each {nv2NumHd.put(it[0] as Long, it[1] / 2)}
		
		// Count 0.5 for double user in one Hop_dong
		sql = "select nv, count(trang_thai) from (" +
			  "select nhan_vien1_id as nv, trang_thai from hop_dong " +
			  "where ngay_ban between '${dateFromSql}' and '${dateToSql}' and doi_truong_id=${dtId} and trang_thai = 'TRA_HANG' " +
			  "union all " +
			  "select nhan_vien2_id as nv, trang_thai from hop_dong " +
			  "where ngay_ban between '${dateFromSql}' and '${dateToSql}' and doi_truong_id=${dtId} and trang_thai = 'TRA_HANG') " +
			  "as hd group by nv;"
		Map nv2NumTraHang = [:]
		new Sql(dataSource).rows(sql)?.each {nv2NumTraHang.put(it[0] as Long, it[1] / 2)}
		// Prepare mapping info for all hopDongs with status = Den_hang
		sql = "select nv, count(trang_thai) from (" +
		"select nhan_vien1_id as nv, trang_thai from hop_dong " +
		"where ngay_ban between '${dateFromSql}' and '${dateToSql}' and doi_truong_id=${dtId} and trang_thai = 'DEN_HANG' " +
		"union all " +
		"select nhan_vien2_id as nv, trang_thai from hop_dong " +
		"where ngay_ban between '${dateFromSql}' and '${dateToSql}' and doi_truong_id=${dtId} and trang_thai = 'DEN_HANG') " +
		"as hd group by nv;"
		Map nv2NumDenHang = [:]
		new Sql(dataSource).rows(sql)?.each {nv2NumDenHang.put(it[0] as Long, it[1] / 2)}
		
		List<ChiTietThuChi> cttcs = ChiTietThuChi.withCriteria {
			projections {
				property("nhanVien.id")
				property("soTien")
				property("quyetToan")
			}
			createAlias("hopDong", "hd")
			between("hd.ngayBan", dateFrom, dateTo)
			'in'("loaiChiTiet", [LoaiChiTietThuChi.DEN_NV, LoaiChiTietThuChi.DEN_DT])
			eq("hd.trangThai", TrangThaiHopDong.DEN_HANG)
		}
		List nvInfo = []
		Map dtInfo = [:]
		int stt = 0
		
		// Add one more line for Hop_Dongs which doiTruong sold only.
		Map dtOnlyInfo = [:]
		List dtOnlySoLanBan = HopDong.withCriteria {
			projections {
				count("id")
			}
			eq("nvBan1.id", dtId)
			isNull("nvDoiTruong")
			between("ngayBan", dateFrom, dateTo)
			ne("trangThai", TrangThaiHopDong.DONG)
		}
		// Add info.
		dtOnlyInfo["soLanBan"] = dtOnlySoLanBan ? dtOnlySoLanBan[0] : 0
		dtOnlyInfo["stt"] = ++stt
		dtOnlyInfo["hoTen"] = doiTruong?.hoTen
		dtOnlyInfo["maNv"] = doiTruong?.maNv
		dtOnlyInfo["soLanTraHang"] = 0
		dtOnlyInfo["soLanDenHang"] = 0
		
		List dtOnlyData = HopDong.withCriteria {
			projections {
				property("trangThai")
				count("id")
			}
			eq("nvBan1.id", dtId)
			isNull("nvDoiTruong")
			between("ngayBan", dateFrom, dateTo)
			'in'("trangThai", [TrangThaiHopDong.TRA_HANG, TrangThaiHopDong.DEN_HANG])
			groupProperty("trangThai")
		}
		dtOnlyData.each {
			if(it[0] == TrangThaiHopDong.TRA_HANG) {
				dtOnlyInfo["soLanTraHang"] = it[1]
			}
			if(it[0] == TrangThaiHopDong.DEN_HANG) {
				dtOnlyInfo["soLanDenHang"] = it[1]
			}
		}
		
		// Chi tiet thu chi for DoiTruong only
		List<ChiTietThuChi> cttcsForDoiTruongOnly = ChiTietThuChi.withCriteria {
			projections {
				property("nhanVien.id")
				property("soTien")
				property("quyetToan")
			}
			createAlias("hopDong", "hd")
			between("hd.ngayBan", dateFrom, dateTo)
			eq("hd.nvBan1.id", dtId)
			isNull("hd.nvDoiTruong")
			'in'("loaiChiTiet", [LoaiChiTietThuChi.DEN_NV, LoaiChiTietThuChi.DEN_DT])
			eq("hd.trangThai", TrangThaiHopDong.DEN_HANG)
		}
		
		Double sumDhForDoiTruongOnly = 0
		Double sumDhNoQtForDoiTruongOnly = 0
		cttcsForDoiTruongOnly.each {
			if (it[0] == dtId) {
				sumDhForDoiTruongOnly += it[1]
				sumDhNoQtForDoiTruongOnly = it[2] ? sumDhNoQtForDoiTruongOnly : sumDhNoQtForDoiTruongOnly + it[1]
			}
		}
		dtOnlyInfo["soTienDenHang"] = sumDhForDoiTruongOnly
		dtOnlyInfo["soTienDenCanQuyetToan"] = sumDhNoQtForDoiTruongOnly
		// Add to result list
		nvInfo << dtOnlyInfo
		
		// Continue to build export data for all nvs in team.
		for (def nv : nvs) {
			Long nvId = nv[0]
			Double sumDh = 0
			Double sumDhNoQt = 0
			cttcs.each {
				if (it[0] == nvId) {
					sumDh += it[1]
					sumDhNoQt = it[2] ? sumDhNoQt : sumDhNoQt + it[1]
				}
			}
						
			Map info = ["hoTen" : nv[1],
					   	"maNv" : nv[2],
						"soLanBan" : nv2NumHd.get(nv[0]) ? nv2NumHd.get(nv[0]) : 0,
					    "soLanTraHang" : nv2NumTraHang.get(nv[0]) ? nv2NumTraHang.get(nv[0]): 0,
					    "soLanDenHang" : nv2NumDenHang.get(nv[0]) ? nv2NumDenHang.get(nv[0]): 0,
					    "soTienDenHang" : sumDh,
					    "soTienDenCanQuyetToan" : sumDhNoQt]
					   
			if(nvId != dtId){
				info["stt"] = ++stt
				nvInfo << info
			} else {
				dtInfo = info
			}
		}
		
		dtInfo["stt"] = stt + 1
		
		List dtSoLanBan = HopDong.withCriteria {
			projections {
				count("id")
			}
			or {
				eq("nvDoiTruong.id", dtId)
				and {
					eq("nvBan1.id", dtId)
					isNull("nvDoiTruong")
				}
			}
			between("ngayBan", dateFrom, dateTo)
			ne("trangThai", TrangThaiHopDong.DONG)
		}
		dtInfo["soLanBan"] = dtSoLanBan ? dtSoLanBan[0] : 0
		
		List dtData = HopDong.withCriteria {
			projections {
				property("trangThai")
				count("id")
			}
			or {
				eq("nvDoiTruong.id", dtId)
				and {
					eq("nvBan1.id", dtId)
					isNull("nvDoiTruong")
				}
			}
			between("ngayBan", dateFrom, dateTo)
			'in'("trangThai", [TrangThaiHopDong.TRA_HANG, TrangThaiHopDong.DEN_HANG])
			groupProperty("trangThai")
		}
		dtData.each {
			if(it[0] == TrangThaiHopDong.TRA_HANG) {
				dtInfo["soLanTraHang"] = it[1]
			}
			if(it[0] == TrangThaiHopDong.DEN_HANG) {
				dtInfo["soLanDenHang"] = it[1]
			}
		}
		
		nvInfo << dtInfo
		exportData.putAt("data", nvInfo)
		return exportData
	}

	def quyetToan = {
		Map result = [:]
		try {
			NhanVien nv = NhanVien.get(Long.valueOf(params["nvId"]))
			List data = getCttcChuaQuyetToan(nv)

			if (!data) {
				String msg = message(code : "default.thientan.messages.noNeedQuyetToanBanHang", args : [nv.hoTen])
				result = getFormMessage(true, "info", msg, null)
			} else {
				List ctIds = data.collect {it[0]}
				Double soTien = data.sum {it[1]}
				ChiTietThuChi.executeUpdate("update ChiTietThuChi ct set ct.quyetToan = true where ct.id in (:ids)", [ids : ctIds])
				String msg = message(code : "default.thientan.messages.quyetToanBanHangSuccess", args : [soTien, nv.hoTen])
				result = getFormMessage(true, "info", msg, null)
			}
		} catch (Exception e) {
			log.error("Error occurred when settling for salesman", e)
			String msg = message(code : "default.thientan.messages.quyetToanBanHangFailed", args : null)
			result = getFormMessage(false, "error", msg, null)
		}
		render result as JSON
	}

	def tinhSoTienQuyetToan = {
		Map result = [:]
		try {
			NhanVien nv = NhanVien.get(Long.valueOf(params["nvId"]))
			List data = getCttcChuaQuyetToan(nv)
			result = [success : true, amount : data.isEmpty() ? 0 : data.sum{it[1]}]
		} catch (Exception e) {
			log.error("Error occurred when calculating unsettle amount of salesman: ", e)
			String msg = message(code : "default.thientan.messages.unableCalcTienQuyetToan", args : null)
			result = getFormMessage(true, "error", msg, null)
		}
		render result as JSON
	}

	private List getCttcChuaQuyetToan(NhanVien nv) {
		def sdfToDate = new java.text.SimpleDateFormat(Constants.TO_DATE_FORMAT)
		Date fromDate = sdfToDate.parse(params["fromDate"])
		Date toDate = sdfToDate.parse(params["toDate"])

		ChiTietThuChi.withCriteria {
			createAlias("hopDong", "hd")
			projections {
				property("id")
				property("soTien")
			}
			eq("quyetToan", false)
			or {
				eq("loaiChiTiet", LoaiChiTietThuChi.DEN_NV)
				eq("loaiChiTiet", LoaiChiTietThuChi.DEN_DT)
			}
			eq("nhanVien", nv)
			between("hd.ngayBan", fromDate, toDate)
		}
	}

	@Override
	protected String getExportFileName() {
		"ChamCongNhanVien"
	}
	
	@Override
	protected String getAdditionalExportName() {
		"_${params.maNv}_"
	}
	
	public NhanVien getLoggedUser() {
		session["user"]
	}
}